﻿using System;
using System.Collections.Generic;
using System.Linq;
using Commands.Settings;

namespace Commands.Scheduling
{
    /// <summary>
    /// Class for scheduling update/suspend/wakeup intervals.
    /// </summary>
    public class Schedule : ISchedule
    {
        #region Constructors

        public Schedule(TimeSpan checkInterval)
        {
            CheckInterval = checkInterval;
            
            SuspendEnabled = false;
            AutoCheckEnabled = true;
            CheckAtStart = false;

            _wakeupTimes = new List<TimeSpan>();
        }

        public Schedule(Settings.TimeSettings timeSettings) : 
            this(timeSettings.AutoCheckEnabled, timeSettings.CheckAtStart, 
                TimeSpan.FromMinutes(timeSettings.MinutesBetweenMailCheck), timeSettings.NeedToSuspend,
                timeSettings.SuspendOnlyOnInactive, TimeSpan.FromMinutes(timeSettings.MinutesBetweenSuspend),
                timeSettings.WakeUpTimes.Select(x => x.TimeSpan))
        {
            _cannotSuspendTimes = timeSettings.CantSuspendTimes;
        }

        public Schedule(bool autoCheckEnabled, bool checkAtStart, TimeSpan checkInterval,
            bool suspendEnabled, bool suspendOnlyInInactive, TimeSpan timeToSuspend,
            IEnumerable<TimeSpan> wakeupTimes)
            : this(checkInterval)
        {
            SuspendEnabled = suspendEnabled;
            AutoCheckEnabled = autoCheckEnabled;
            CheckAtStart = checkAtStart;
            SuspendOnlyOnInactive = suspendOnlyInInactive;

            TimeToSuspend = timeToSuspend;
            _SetWakeUpTimes(wakeupTimes);
        }

        #endregion

        #region Public members

        /// <summary>
        /// Property changed events.
        /// </summary>
        public event Action SuspendSettingsChanged;
        public event Action CheckSettingsChanged;

        /// <summary>
        /// Time in which computer must be suspended if no commands was received.
        /// </summary>
        public TimeSpan TimeToSuspend
        {
            get { return _timeToSuspendDefault; }
            set
            {
                _timeToSuspendDefault = value;
                _OnSuspendChanged();
            }
        }

        /// <summary>
        /// Interval in which mailbox must be checked.
        /// </summary>
        public TimeSpan CheckInterval
        {
            get { return _checkInterval; }
            set
            {
                _checkInterval = value;

                if (CheckSettingsChanged != null)
                    CheckSettingsChanged();
            }
        }

        /// <summary>
        /// Flag, showing should suspend only after inactive time.
        /// </summary>
        public bool SuspendOnlyOnInactive
        {
            get
            {
                return _suspendOnlyOnInactive;
            }
            set
            {
                _suspendOnlyOnInactive = value;
            }
        }

        /// <summary>
        /// Flag, showing should commands be checked on application start.
        /// </summary>
        public bool CheckAtStart { get; set; }

        /// <summary>
        /// Flag, which show that computer must be suspended.
        /// </summary>
        public bool SuspendEnabled
        {
            get
            {
                return _suspendEnabled;
            }
            set
            {
                _suspendEnabled = value;
                _OnSuspendChanged();
            }
        }

        /// <summary>
        /// Flag, which show that auto-check is enabled.
        /// </summary>
        public bool AutoCheckEnabled
        {
            get
            {
                return _autoCheckEnabled;
            }
            set
            {
                _autoCheckEnabled = value;
            }
        }

        public DateTime NextCheckTime
        {
            get;
            set;
        }

        public DateTime SuspendTime
        {
            get;
            set;
        }

        /// <summary>
        /// Get time on which computer must be waked up.
        /// </summary>
        /// <param name="suspendTme">Time on which computer will be suspended.</param>
        public DateTime GetWakeupTime(DateTime suspendTme)
        {
            // Prepare wake up times collection.
            var times = _wakeupTimes;

            // Find first wakeup time, which is greater then suspend time.
            TimeSpan time = times.FirstOrDefault(x => x > suspendTme.TimeOfDay);

            // If we found time - return it in current day, 
            // otherwise - return first time on the next day.
            return time != TimeSpan.Zero ? 
                DateTime.Now.Date.Add(time) : 
                DateTime.Now.Date.AddDays(1).Add(times.First());
        }

        /// <summary>
        /// Check that remcom can be suspended now.
        /// </summary>
        public bool CanBeSuspendedNow()
        {
            return !_cannotSuspendTimes.Any(range => range.IsTimeInsideInterval(DateTime.Now));
        }

        #endregion

        #region Private members

        private TimeSpan _checkInterval;
        private TimeSpan _timeToSuspendDefault;
        private TimeSpan _timeToSuspendSucces;

        private bool _suspendEnabled;
        private bool _autoCheckEnabled;
        private bool _suspendOnlyOnInactive;

        private void _SetWakeUpTimes(IEnumerable<TimeSpan> times)
        {
            var list = times.ToList();

            //// TC
            //list.Add(TimeSpan.FromHours(10.5));
            //list.Add(TimeSpan.FromHours(10.7));

            // Remove all spans which are longer then 1 day.
            list.RemoveAll(x => x.TotalDays > 1);

            // Sort remaining spans.
            list.Sort();

            _wakeupTimes = times;
        }
        private IEnumerable<TimeSpan> _wakeupTimes;

        private IEnumerable<TimeIntervals> _cannotSuspendTimes;

        private void _OnSuspendChanged()
        {
            if (SuspendSettingsChanged != null)
                SuspendSettingsChanged();
        }

        #endregion
    }
}
